package ru.amse.tsyganov.jumleditor.graphicaleditor;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;

import ru.amse.tsyganov.jumleditor.commands.ViewsManager;
import ru.amse.tsyganov.jumleditor.model.ActivityGraph;
import ru.amse.tsyganov.jumleditor.model.Partition;
import ru.amse.tsyganov.jumleditor.model.State;
import ru.amse.tsyganov.jumleditor.model.Transition;
import ru.amse.tsyganov.jumleditor.properties.Property;
import ru.amse.tsyganov.jumleditor.properties.PropertyChangeListener;
import ru.amse.tsyganov.jumleditor.view.ActivityGraphView;
import ru.amse.tsyganov.jumleditor.view.Colors;
import ru.amse.tsyganov.jumleditor.view.StateView;
import ru.amse.tsyganov.jumleditor.view.TransitionView;

public class GraphicalEditor extends Composite {

	private static final int TOOL_BAR_HEIGHT = 25;
	
	class MouseEventsHandler implements MouseListener, MouseMoveListener {
		
		public void mouseDoubleClick(MouseEvent e) {
			assert e != null;
			editorMode.mouseDoubleClick(e);
			canvas.redraw();
		}
		
		public void mouseDown(MouseEvent e) {
			assert e != null;
			editorMode.mouseDown(e);
			canvas.redraw();
		}
		
		public void mouseUp(MouseEvent e) {
			assert e != null;
			editorMode.mouseUp(e);
			canvas.redraw();
		}
		
		public void mouseMove(MouseEvent e) {
			assert e != null;
			editorMode.mouseMove(e);
			canvas.redraw();
		}		
	}
	
	private final EditorMode addActionStateMode;
	
	private final EditorMode addFinalStateMode;
	
	private final EditorMode addForkJoinStateMode;
	
	private final EditorMode addInitialStateMode;
	
	private final EditorMode addDecisionMode;
	
	private final EditorMode addTransitionMode;
	
	private final EditorMode areaSelectionMode;
	
	private final EditorMode selectionMode;
	
	private final EditorMode textInputMode;
	
	private final Canvas canvas;
	
	private final Text textInputField;
	
	private final ActivityGraphView<ActivityGraph> activityGraphView;
	
	private final ToolBar toolBar;
	
	private final ViewsManager viewsManager;
	
	private EditorMode editorMode;
		
	private boolean shiftOrCtrlPressed = false;
	
	private final PropertyChangeListener propertyChangeListener = 
		new PropertyChangeListener() {

		public void propertyChanged(Property p) {
			canvas.redraw();
		}
		
	};

	public GraphicalEditor(
			Composite parent,
			ActivityGraphView<ActivityGraph> activityGraphView,
			ViewsManager viewsManager) {
				
		super(parent, SWT.NONE);
		
		if (parent == null || activityGraphView == null
			|| viewsManager == null) {
			
			throw new IllegalArgumentException();
		}
		
		this.activityGraphView = activityGraphView;
		
		addPropertyChangedListeners();
		
		this.viewsManager = viewsManager;
		
		canvas = new Canvas(this, SWT.NONE | SWT.DOUBLE_BUFFERED);
		toolBar = new ToolBar(this, SWT.NONE);
		textInputField = new Text(canvas, SWT.SINGLE | SWT.BORDER);
		
		initToolBar();
		initCanvas();
		
		FormLayout compositeFormLayout = new FormLayout();
		compositeFormLayout.marginHeight = 0;
		compositeFormLayout.marginWidth = 0;
		compositeFormLayout.marginBottom = 0;
		compositeFormLayout.marginLeft = 0;
		compositeFormLayout.marginRight = 0;
		compositeFormLayout.marginTop = 0;
		setLayout(compositeFormLayout);
				
		addActionStateMode = new AddActionStateMode(this);
		addFinalStateMode = new AddFinalStateMode(this);
		addForkJoinStateMode = new AddForkJoinStateMode(this);
		addInitialStateMode = new AddInitialStateMode(this);
		addTransitionMode = new AddTransitionMode(this);
		addDecisionMode = new AddDecisionMode(this);
		areaSelectionMode = new AreaSelectionMode(this);
		selectionMode = new SelectionMode(this);
		textInputMode = new TextInputMode(this);
		
		editorMode = selectionMode;
	}

	private void addPropertyChangedListeners() {
		for (Partition p : activityGraphView.getModel().getPartitions()) {
			
			for (State s : p.getContents()) {
				StateView<? extends State> stateView = 
					activityGraphView.getModelViewMap().getStatesMap().get(s);
				
				stateView.addPropertyListener(propertyChangeListener);
				
				for (Transition t : s.getStateVertex().getOutgoing()) {
					TransitionView<? extends Transition> transitionView =
						activityGraphView.getModelViewMap().getTransitionsMap().get(t);
					
					transitionView.addPropertyListener(propertyChangeListener);
				}
			}
		}
	}
	
	private void initCanvas() {
		
		/*canvas.*/addKeyListener(new KeyListener() {
			public void keyPressed(KeyEvent e) {
				if (e.keyCode == SWT.SHIFT || e.keyCode == SWT.CTRL){
					shiftOrCtrlPressed = true;
				}
				editorMode.keyPressed(e);
				canvas.redraw();
			}
			public void keyReleased(KeyEvent e) {
				if (e.keyCode == SWT.SHIFT || e.keyCode == SWT.CTRL){
					shiftOrCtrlPressed = false;
				}
				editorMode.keyReleased(e);
				canvas.redraw();
			}
		});
				
		Color lightGrayColor = Colors.getWhiteColor();
		
		canvas.setBackground(lightGrayColor);
		canvas.addPaintListener(new PaintListener() {
			public void paintControl(PaintEvent e) {
				activityGraphView.paint(e.gc);
				editorMode.paintControl(e);
			}
		});
		
		MouseEventsHandler mouseEventsHandler = new MouseEventsHandler();
		canvas.addMouseListener(mouseEventsHandler);
		canvas.addMouseMoveListener(mouseEventsHandler);
		
		FormData canvasFormData = new FormData();
		canvasFormData.left = new FormAttachment(0, 0);
		canvasFormData.right = new FormAttachment(100, 0);
		canvasFormData.bottom = new FormAttachment(100, 0);
		canvasFormData.top = new FormAttachment(toolBar, 0);
		canvas.setLayoutData(canvasFormData);
		
	}
	
	private void initToolBar() {	
		
		FormData toolBarFormData = new FormData();
		toolBarFormData.left = new FormAttachment(0, 0);
		toolBarFormData.right = new FormAttachment(100, 0);
		toolBarFormData.bottom = new FormAttachment(0, TOOL_BAR_HEIGHT);
		toolBarFormData.top = new FormAttachment(0, 0);
		toolBar.setLayoutData(toolBarFormData);
		
		ToolItem undoItem = new ToolItem(toolBar, SWT.PUSH);
		undoItem.setImage(new Image(null, "icons/undo_edit.gif"));
		undoItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				viewsManager.undo();
				canvas.redraw();
			}
			
		});
		ToolItem redoItem = new ToolItem(toolBar, SWT.PUSH);
		redoItem.setImage(new Image(null, "icons/redo_edit.gif"));
		redoItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				viewsManager.redo();
				canvas.redraw();
			}
			
		});
		
		ToolItem selectionModeItem = new ToolItem(toolBar, SWT.PUSH);
		selectionModeItem.setImage(new Image(null, "icons/select.gif"));
		selectionModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = selectionMode;
			}
			
		});
		
		ToolItem addActionStateModeItem = new ToolItem(toolBar, SWT.PUSH);
		addActionStateModeItem.setImage(new Image(null, "icons/activity.gif"));
		addActionStateModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addActionStateMode;
			}
			
		});
		
		ToolItem addInitialStateModeItem = new ToolItem(toolBar, SWT.PUSH);
		addInitialStateModeItem.setImage(new Image(null, "icons/initialState.gif"));
		addInitialStateModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addInitialStateMode;
			}
			
		});
		
		ToolItem addFinalStateModeItem = new ToolItem(toolBar, SWT.PUSH);
		addFinalStateModeItem.setImage(new Image(null, "icons/finalState.gif"));
		addFinalStateModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addFinalStateMode;
			}
			
		});
		
		ToolItem addForkJoinStateModeItem = new ToolItem(toolBar, SWT.PUSH);
		addForkJoinStateModeItem.setImage(new Image(null, "icons/join.gif"));
		addForkJoinStateModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addForkJoinStateMode;
			}
			
		});
		
		ToolItem addDecisionModeItem = new ToolItem(toolBar, SWT.PUSH);
		addDecisionModeItem.setImage(new Image(null, "icons/decision.gif"));
		addDecisionModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addDecisionMode;
			}
			
		});
		
		ToolItem addTransitionModeItem = new ToolItem(toolBar, SWT.PUSH);
		addTransitionModeItem.setImage(new Image(null, "icons/transition.gif"));
		addTransitionModeItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				editorMode = addTransitionMode;
			}
			
		});
		
		assert toolBar != null;
	}
	
	/*package*/ void setEditorMode(EditorMode editorMode) {
		editorMode.initState();
		this.editorMode = editorMode;
	}

	/*package*/ ActivityGraphView<ActivityGraph> getActivityGraphView() {
		return activityGraphView;
	}

	/*package*/ Canvas getCanvas() {
		return canvas;
	}

	/*package*/ ViewsManager getViewsManager() {
		return viewsManager;
	}
	
	/*package*/ boolean isShiftOrCtrlPressed() {
		return shiftOrCtrlPressed;
	}

	/*package*/ Text getTextInputField() {
		return textInputField;
	}
	
	public boolean addSelectionListener(ItemsSelectionListener listener) {
		return activityGraphView.getSelectedItems().addSelectionListener(listener);
	}
	
	public boolean removeSelectionListener(ItemsSelectionListener listener) {
		return activityGraphView.getSelectedItems().removeSelectionListener(listener);
	}

	/*package*/ EditorMode getAddActionStateMode() {
		return addActionStateMode;
	}

	/*package*/ EditorMode getAddFinalStateMode() {
		return addFinalStateMode;
	}

	/*package*/ EditorMode getAddForkJoinStateMode() {
		return addForkJoinStateMode;
	}

	/*package*/ EditorMode getAddInitialStateMode() {
		return addInitialStateMode;
	}

	/*package*/ EditorMode getAddTransitionMode() {
		return addTransitionMode;
	}

	/*package*/ EditorMode getAreaSelectionMode() {
		return areaSelectionMode;
	}

	/*package*/ EditorMode getTextInputMode() {
		return textInputMode;
	}

	/*package*/ EditorMode getSelectionMode() {
		return selectionMode;
	}

	/*package*/ PropertyChangeListener getPropertyChangeListener() {
		return propertyChangeListener;
	}
}
